package org.example.handler;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.example.annotation.RequestBody;
import org.example.annotation.ResponseBody;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 方法处理器
 * 对处理请求URL的方法进行封装，进行
 *
 * @author hrz
 * @since 2021/3/3 0003
 */
public class MethodHandler {

    private Method method;

    private Object object;

    private String requestPath;

    private ObjectMapper mapper;

    public MethodHandler(Method method, Object object, String requestPath) {
        this.method = method;
        this.object = object;
        this.requestPath = requestPath;
        this.mapper = new ObjectMapper();

        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }

    public void handle(HttpServletRequest req, HttpServletResponse resp) throws Throwable {
        if (method.getParameterCount() == 0) {
            Object result = this.method.invoke(object);
            handleResult(result, req, resp);
            return;
        }
        // 根据处理请求的方法的参数，获取参数值列表
        List<Object> args = new LinkedList<>();
        Parameter[] parameterTypes = method.getParameters();
        for (Parameter parameter : parameterTypes) {
            // 根据参数名称和类型，从请求中获取属性值
            args.add(getParameter(parameter, req));
        }
        // 执行处理请求的方法，拿到处理结果
        Object result = this.method.invoke(object, args.toArray());
        handleResult(result, req, resp);
    }



    /**
     * @return 处理请求的方法名
     */
    public String name() {
        return method.getName();
    }

    private Object getParameter(Parameter parameter, HttpServletRequest req) throws IOException {

        if (parameter.isAnnotationPresent(RequestBody.class)) {
            // 获取请求流数据
            InputStream is = req.getInputStream();
            String requestBody = new BufferedReader(new InputStreamReader(is, req.getCharacterEncoding()))
                    .lines().collect(Collectors.joining("\n"));
            if (StringUtils.isEmpty(requestBody)) {
                return null;
            }
            return mapper.readValue(requestBody, (Class<?>)parameter.getParameterizedType());
        }

        if (parameter.getType() == HttpServletRequest.class) {
            return req;
        }

        // 获取参数的类型
        if (parameter.getParameterizedType() == String.class) {
            // 根据参数名称，从 HttpServletRequest 获取数据，并根据类型实例化返回
            return req.getParameter(parameter.getName());
        }

        if (parameter.getParameterizedType() == Integer.class) {
            // 根据参数名称，从 HttpServletRequest 获取数据，并根据类型实例化返回
            String value = req.getParameter(parameter.getName());
            return value == null ? null : Integer.valueOf(value);
        }

        if (parameter.getParameterizedType() == HttpServletRequest.class) {
            // 根据参数名称，从 HttpServletRequest 获取数据，并根据类型实例化返回
            return req;
        }

        return null;
    }

    /**
     * 处理请求处理方法的返回结果
     *
     * @param result 请求方法返回的结果
     * @param req Servlet请求对象
     * @param resp Servlet响应对象
     */
    private void handleResult(Object result, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
        if (req.getCookies() != null) {
            Arrays.stream(req.getCookies()).forEach(resp::addCookie);
        }
        if (method.isAnnotationPresent(ResponseBody.class)) {
            if (result instanceof String) {
                resp.getWriter().write((String) result);
                return;
            }
            String resultJson = mapper.writeValueAsString(result);
            resp.setContentType("application/json; charset=UTF-8");
            resp.getWriter().write(resultJson);
        } else {
            req.getRequestDispatcher("/"+result).forward(req, resp);
        }
    }

}
